﻿using Nes.Core.Mappers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nes.Core
{
    public class RomInfo 
    {
        public static RomInfo CreateRomInfo(Byte[] data)
        {
            var romInfo = new RomInfo
            {
                PRG = data[4],
                CHR = data[5],
                Mirror = (data[6] & 0b1) == 0 ? Mirror.HORIZONTAL : Mirror.VERTICAL,
                HasBatteryBank = (data[6] & 0b10) == 1,
                IsTrained = (data[6] & 0b100) == 1,
                FourScreen = (data[6] & 0b1000) == 1,
                Mapper = data[6] >> 4 | data[7] & 0b11110000
            };
            // 8 - 15 位暂时不处理

            return romInfo;
        }

        public Int32 PRG { get; set; }
        public Int32 CHR { get; set; }

        /// <summary>
        /// MapperType
        /// </summary>
        public Int32 Mapper { get; set; }

        public Mirror Mirror { get; set; }

        public Boolean HasBatteryBank { get; set; }

        public Boolean IsTrained { get; set; }
        public Boolean FourScreen { get; set; }
    }

    public interface ICartridge
    {
        Byte[] PRG { get; }
        Byte[] CHR { get; }

        RomInfo HeaderInfo { get; }
        IMapper Mapper { get; }
    }


    /// <summary>
    /// 卡带实现
    /// </summary>
    public class Cartridge : ICartridge
    {
        private Byte[] prg;
        private Byte[] chr;
        private RomInfo headerInfo;
        private IMapper mapper;

        public RomInfo HeaderInfo { get { return headerInfo; } }

        public IMapper Mapper { get { return mapper; } }

        /// <summary>
        /// 程序数据 0x8000 - 0xffff
        /// </summary>
        public Byte[] PRG { get { return prg; } }
        /// <summary>
        /// 图像数据 0x0000 - 0x2000
        /// </summary>
        public Byte[] CHR { get { return chr; } }

        public Cartridge(Byte[] data, Byte[] sram)
        {
            LoadData(data, sram);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="data"></param>
        /// <param name="sram"></param>
        /// <exception cref="Exception"></exception>
        public void LoadData(Byte[] data, Byte[] sram)
        {
            // data 信息格式
            // 0 - 3
            // 4E 45 53 1A -> NES␚
            if (data[0] != 78 || data[1] != 69 || data[2] != 83 || data[3] != 26)
            {
                throw new Exception("Not a valid NES ROM.");
            }
            headerInfo = RomInfo.CreateRomInfo(data[0..16]);

            var offset = HeaderInfo.IsTrained ? 512 + 16 : 16;
            // 根据RomInfo读取PGR 和 CHR
            var prgLength = HeaderInfo.PRG * 16 * 1024;
            var chrLength = HeaderInfo.CHR * 8 * 1024;

            prg = data[offset..(offset + prgLength)];
            chr = data[(offset + prgLength)..(offset + prgLength + chrLength)];

            mapper = MapperUtil.CreateMapper(this, sram);

        }
    }
}
